﻿using System;
using System.Windows;
using System.Windows.Controls;
using Dsl.Ped.Framework.Projections;
using Dsl.Ped.Framework.Projections.ObjectProjections;
using Dsl.Ped.Framework.Projections.ObjectProjections.Block;
using Dsl.Ped.Framework.Projections.ObjectProjections.Headered;

namespace Dsl.Ped.Framework.Common
{
    /// <summary>
    /// Factory for the creation of object-based projections
    /// </summary>
    public class ObjectProjectionFactory : ProjectionFactoryBase
    {
        /// <summary>
        /// Initializes properly object-based projection
        /// </summary>
        /// <typeparam name="T">Type of the projection (like TextBox, Label, etc.)</typeparam>
        /// <param name="projectionHint">Hint for the creation of a projection</param>
        /// <param name="type">Type of a property which the projection is bound to</param>
        /// <returns><see cref="FrameworkElement"/> representing the object-based projection</returns>
        public static FrameworkElement InitializeObjectProjection<T>(
            IObjectProjectionHint<T> projectionHint, Type type)
            where T : FrameworkElement, new()
        {
            var projection = InitializeProjection(projectionHint, null);
            return projection;
        }

        #region Block

        /// <summary>
        /// Gets the block projection. A projection in which the objects are organized in a block
        /// </summary>
        /// <param name="headerProjection">Projection of the header of a block</param>
        /// <param name="content">Content of the projection</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="BlockProjection"/></param>
        /// <returns>Block projection</returns>
        public static IProjection GetBlockProjection(IProjection headerProjection, object content, Action<BlockProjection> customConfiguration)
        {
            var projectionHint = new ObjectProjectionHint<BlockProjection>(
                (blockProjection, type) =>
                {
                    blockProjection.Orientation = Orientation.Horizontal;
                    blockProjection.HeaderProjection = headerProjection;
                    blockProjection.Content = content;
                },
                customConfiguration);

            return new Projection() { View = InitializeObjectProjection(projectionHint, null) };
        }

        /// <summary>
        /// Gets the block projection. A projection in which the objects are organized in a block
        /// </summary>
        /// <param name="headerProjection">Projection of the header of a block</param>
        /// <param name="contentProjection">Content of the projection</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="BlockProjection"/></param>
        /// <returns>Block projection</returns>
        public static IProjection GetBlockProjection(IProjection headerProjection, IProjection contentProjection, Action<BlockProjection> customConfiguration = null)
        {
            return GetBlockProjection(headerProjection, content: contentProjection, customConfiguration: customConfiguration);
        }

        /// <summary>
        /// Gets the block projection. A projection in which the objects are organized in a block
        /// </summary>
        /// <param name="headerProjection">Projection of the header of a block</param>
        /// <param name="projectiveObject">Projective object that represents a content of the block</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="BlockProjection"/></param>
        /// <returns>Block projection</returns>
        public static IProjection GetBlockProjection(IProjection headerProjection, IProjective projectiveObject, Action<BlockProjection> customConfiguration = null)
        {
            return GetBlockProjection(headerProjection, content: projectiveObject, customConfiguration: customConfiguration);
        }

        /// <summary>
        /// Gets the block projection. A projection in which the objects are organized in a block
        /// </summary>
        /// <param name="headerProjection">Projection of the header of a block</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="BlockProjection"/></param>
        /// <returns>Block projection</returns>
        public static IProjection GetBlockProjection(IProjection headerProjection, Action<BlockProjection> customConfiguration = null)
        {
            return GetBlockProjection(headerProjection, content: null, customConfiguration: customConfiguration);
        }

        #endregion

        #region Headered

        /// <summary>
        /// Gets the headered projection. A projection in which the content is provided with a header
        /// </summary>
        /// <param name="header">Header of the projection</param>
        /// <param name="content">Content of the projection</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="HeaderedProjection"/></param>
        /// <returns>Headered projection</returns>
        public static IProjection GetHeaderedProjection(object header, object content, Action<HeaderedProjection> customConfiguration = null)
        {
            var projectionHint = new ObjectProjectionHint<Projections.ObjectProjections.Headered.HeaderedProjection>(
                (headeredProjection, type) =>
                {
                    headeredProjection.Orientation = Orientation.Horizontal;
                    headeredProjection.Header = header;
                    headeredProjection.Content = content;
                },
                customConfiguration ?? (hp => { }));

            return new Projection() { View = InitializeProjection(projectionHint, null) };
        }

        /// <summary>
        /// Gets the headered projection. A projection in which the content is provided with a header
        /// </summary>
        /// <param name="header">Header of the projection</param>
        /// <param name="contentProjection">Content of the projection</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="HeaderedProjection"/></param>
        /// <returns>Headered projection</returns>
        public static IProjection GetHeaderedProjection(object header, IProjection contentProjection, Action<HeaderedProjection> customConfiguration = null)
        {
            return GetHeaderedProjection(header, (object)
                contentProjection, customConfiguration);
        }

        /// <summary>
        /// Gets the headered projection. A projection in which the content is provided with a header
        /// </summary>
        /// <param name="header">Header of the projection</param>
        /// <param name="projectiveObject">ProjectiveObject representing the content of a projection</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="HeaderedProjection"/><</param>
        /// <returns>Headered projection</returns>
        public static IProjection GetHeaderedProjection(object header, IProjective projectiveObject, Action<HeaderedProjection> customConfiguration = null)
        {
            return GetHeaderedProjection(header, (object)projectiveObject, customConfiguration);
        }

        #endregion
    }
}
